package com.xu.sso.server.service.impl;

import com.xu.sso.core.config.SSOConfig;
import com.xu.sso.core.config.SystemConfig;
import com.xu.sso.core.enums.AuthTypeEnum;
import com.xu.sso.core.enums.ErrorMessage;
import com.xu.sso.core.enums.StatusEnum;
import com.xu.sso.core.exception.SSOException;
import com.xu.sso.core.model.BaseResponse;
import com.xu.sso.core.model.UserLoginInfo;
import com.xu.sso.core.utils.*;
import com.xu.sso.db.model.base.App;
import com.xu.sso.db.model.base.AppRole;
import com.xu.sso.db.model.base.Department;
import com.xu.sso.db.model.base.User;
import com.xu.sso.db.service.AppDBService;
import com.xu.sso.db.service.DepartmentDBService;
import com.xu.sso.db.service.RoleDBService;
import com.xu.sso.db.service.UserDBService;
import com.xu.sso.server.model.request.ApiLogoutRequest;
import com.xu.sso.server.model.request.LoginRequest;
import com.xu.sso.server.model.response.LoginResponse;
import com.xu.sso.server.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class LoginServiceImpl implements LoginService {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private UserDBService userDBService;
    @Autowired
    private RoleDBService roleDBService;
    @Autowired
    private AppDBService appDBService;
    @Autowired
    private DepartmentDBService departmentDBService;
    @Autowired
    private SystemConfig config;
    @Autowired
    private MailUtil mailUtil;


    @Override
    public LoginResponse checkLogin(HttpServletRequest request) throws SSOException {

        LoginResponse loginResponse = new LoginResponse();

        String token = CookiesUtil.getValue(request, SSOConfig.TOKEN);
        if (StringUtils.isNotBlank(token) && !"null".equalsIgnoreCase(token)) {
            // 查询缓存
            UserLoginInfo userLoginInfo = redisUtil.getObjectValue(SSOConfig.REDIS_SET + token, UserLoginInfo.class);
            if (null == userLoginInfo) {
                throw ExceptionUtil.createException(ErrorMessage.LogonExpires);
            }
            String appId = request.getParameter(SSOConfig.APP_ID);
            App app = appDBService.findAppByAppId(appId);
            if (app.getStatus().equals(StatusEnum.Stop.getCode())) {
                throw ExceptionUtil.createException(ErrorMessage.AppStop);
            }

            // 判断是否为同一IP
//            String ip = IpAddrUtil.getIpAddress(request);
//            if (!ip.equals(userLoginInfo.getIp())) {
//                throw ExceptionUtil.createException(ErrorMessage.IpAddrError);
//            }

            // 判断是否已经登录过此应用
            if (userLoginInfo.getAppRoles().containsKey(appId)) {
                loginResponse.setToken(token);
                loginResponse.setRedirectUrl(app.getAppPath());
                return loginResponse;
            }

            // 判断是否是私有应用
            if (app.getAuthType().equals(AuthTypeEnum.Private.getCode())) {
                // 判断用户是否有此应用权限
                boolean checkAppPower = appDBService.checkAppPowerByUser(appId, userLoginInfo.getUsername());
                if (!checkAppPower) {
                    throw ExceptionUtil.createException(ErrorMessage.NoAppPower);
                }
            }

            // 获取角色信息
            List<AppRole> roles = roleDBService.findInAppRoles(appId, userLoginInfo.getUsername());
            userLoginInfo.getAppRoles().put(appId, roles.stream().map(AppRole::getRoleName).collect(Collectors.toList()));
            // 更新到缓存
            boolean result = redisUtil.set(SSOConfig.REDIS_SET + token, userLoginInfo, SSOConfig.EXPIRATION);
            if (!result) {
                log.error(">>>>>>>>>>>>>>>>>LoginServiceImpl.checkLogin update token to redis error...");
                throw ExceptionUtil.createException(ErrorMessage.RedisError);
            }

            loginResponse.setToken(token);
            loginResponse.setRedirectUrl(app.getAppPath());
        }
        return loginResponse;
    }

    @Override
    public LoginResponse login(LoginRequest request) throws SSOException {

        LoginResponse loginResponse = new LoginResponse();

        // 用户验证
        String password = LoginUtil.encodePassword(request.getUsername(), request.getPassword());
        User user = userDBService.findUserByUsername(request.getUsername());
        if (null == user) {
            log.info(">>>>>>>>>>>>>>>>>LoginServiceImpl.login error not found user by username == {}", request.getUsername());
            throw ExceptionUtil.createException(ErrorMessage.NoUserError);
        }
        if (!password.equals(user.getPassword())) {
            throw ExceptionUtil.createException(ErrorMessage.PasswordError);
        }

        // 验证应用状态
        App app = appDBService.findAppByAppId(request.getAppId());
        if (app.getStatus().equals(StatusEnum.Stop.getCode())) {
            throw ExceptionUtil.createException(ErrorMessage.AppStop);
        }

        // 判断是否是私有应用
        if (app.getAuthType().equals(AuthTypeEnum.Private.getCode())) {
            // 判断用户是否有此应用权限
            boolean checkAppPower = appDBService.checkAppPowerByUser(request.getAppId(), request.getUsername());
            if (!checkAppPower) {
                throw ExceptionUtil.createException(ErrorMessage.NoAppPower);
            }
        }

        // 获取角色信息
        Map<String, List<String>> appRoles = new HashMap<>();
        List<AppRole> roles = roleDBService.findInAppRoles(request.getAppId(), request.getUsername());
        appRoles.put(request.getAppId(), roles.stream().map(AppRole::getRoleName).collect(Collectors.toList()));

        // 生成token，添加到缓存
        String token = LoginUtil.createToken();
//        String ip = IpAddrUtil.getIpAddress(servletRequest);
        UserLoginInfo userLoginInfo = new UserLoginInfo();
        userLoginInfo.setAppRoles(appRoles);
        userLoginInfo.setUsername(request.getUsername());
        userLoginInfo.setName(user.getName());
//        userLoginInfo.setIp(ip);

        boolean result = redisUtil.set(SSOConfig.REDIS_SET + token, userLoginInfo, SSOConfig.EXPIRATION);
        if (!result) {
            log.error(">>>>>>>>>>>>>>>>>LoginServiceImpl.login put token to redis error...");
            throw ExceptionUtil.createException(ErrorMessage.RedisError);
        }

        loginResponse.setToken(token);
        loginResponse.setRedirectUrl(app.getAppPath());
        log.info(">>>>>>>>>>>>>>>>>LoginServiceImpl.login success...{}", request.getUsername());
        return loginResponse;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response) {
        // 删除cookie
        CookiesUtil.remove(request, response, SSOConfig.TOKEN);
        // 清除缓存
        redisUtil.del(SSOConfig.REDIS_SET + request.getParameter(SSOConfig.TOKEN));
    }

    @Override
    public BaseResponse register(User user) throws SSOException {
        BaseResponse response = new BaseResponse();
        // 是否验证公司邮箱
        if (StringUtils.isNotBlank(config.getMailSuffix()) && !user.getUsername().endsWith(config.getMailSuffix())) {
            throw ExceptionUtil.createException(ErrorMessage.EmailAddressError);
        }
        // 判断用户是否已经存在
        User orgUser = userDBService.findUserByUsername(user.getUsername());
        if (null != orgUser) {
            throw ExceptionUtil.createException(ErrorMessage.UserExist);
        }
        // 创建用户
        user.setMail(user.getUsername());
        String pwd = RandomStringUtils.randomAlphanumeric(12);
        user.setPassword(LoginUtil.encodePassword(user.getUsername(), pwd));
        userDBService.createUser(user);
        // 发送邮件
        mailUtil.send(user.getMail(), "新用户注册", user.getUsername(), pwd);

        response.setCode(ErrorMessage.Success.getErrCode());
        response.setMsg(ErrorMessage.Success.getErrMsg());
        return response;
    }

    @Override
    public BaseResponse reset(String username, String mail) throws SSOException {
        BaseResponse response = new BaseResponse();
        // 验证用户
        User user = userDBService.findUserByUsername(username);
        if (null == user) {
            throw ExceptionUtil.createException(ErrorMessage.NoUserError);
        }
        if (!mail.equals(user.getMail())) {
            throw ExceptionUtil.createException(ErrorMessage.UserEmailNotMatch);
        }
        // 更新密码
        String pwd = RandomStringUtils.randomAlphanumeric(12);
        userDBService.updatePassword(username, LoginUtil.encodePassword(user.getUsername(), pwd));
        // 发送邮件
        mailUtil.send(mail, "重置密码", username, pwd);

        response.setCode(ErrorMessage.Success.getErrCode());
        response.setMsg(ErrorMessage.Success.getErrMsg());
        return response;
    }

    @Override
    public List<Department> findAllDepartments() {
        return departmentDBService.findAllDepartment();
    }

    @Override
    public BaseResponse logout(ApiLogoutRequest request) throws SSOException {
        // 清除缓存
        redisUtil.del(SSOConfig.REDIS_SET + request.getToken());
        return new BaseResponse();
    }

}
